﻿using System;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;

namespace PFD
{
    public class RenderDeviceX : IRenderDevice
    {

        // the d3d rendering device and device state
        private Device device;

        //  Maintain a list of Texture Resources
        List<RenderTextureX> textures = new List<RenderTextureX>();

        //  Maintain a list of Vertex Buffer Resources
        List<VertexBufferX> vertexBuffers = new List<VertexBufferX>();

        //  Indicate this object was disposed already
        bool Disposed = false;

        void OnDeviceReset(object sender, EventArgs e)
        {
            int memFree = device.GetAvailablePoolMem(Pool.SystemMemory);
            Debug.WriteLine("Free System Memory after Device Reset is " + memFree.ToString() + " Bytes");

            //Reset textures and vertex pools
            device.RenderState.Lighting = false;
            device.RenderState.TexturePerspective = true;
            EnableBlending(true);
            device.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            device.RenderState.SourceBlend = Blend.SourceAlpha;
            EnableDepthTest(true);
            EnableDithering(true);
            EnableShading(SHADE_MODE.Flat);
        }

        public Device GetDevice() { return device; }

        #region IRenderDevice Members

        public bool Initialize(Form f)
        {
            PresentParameters pp = new PresentParameters();
            pp.Windowed = true;
            pp.SwapEffect = SwapEffect.Flip;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
            pp.EnableAutoDepthStencil = true;

            device = new Device(0, DeviceType.Default, f, CreateFlags.None, pp);

            device.DeviceReset += new EventHandler(OnDeviceReset);

            device.DeviceLost += new EventHandler(device_DeviceLost);
//            device.DeviceResizing += new System.ComponentModel.CancelEventHandler(device_DeviceResizing);
            OnDeviceReset(device, EventArgs.Empty);

            return true;
        }

        void device_DeviceResizing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Debug.WriteLine("Device is Resizing !");


        }

        void device_DeviceLost(object sender, EventArgs e)
        {
            Debug.WriteLine("Device is lost !");

            PresentParameters pp = new PresentParameters();
            pp.Windowed = true;
            pp.SwapEffect = SwapEffect.Flip;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
            pp.EnableAutoDepthStencil = true;
            Device d = (Device)sender;
            foreach (RenderTextureX tex in textures)
                tex.OnDeviceLost(d);

            foreach (VertexBufferX vb in vertexBuffers)
                vb.OnDeviceLost(d);
        }

        public bool SetViewport(Rectangle r)
        {
            Viewport vp = new Viewport();
            vp.X = r.Left;
            vp.Y = r.Top;
            vp.Width = r.Width;
            vp.Height = r.Height;
            device.Viewport = vp;
            return true;
        }

        public bool SetOrthographicProjection(float left, float right, float top, float bottom, float near, float far)
        {
            device.Transform.Projection = Matrix.OrthoOffCenterLH(left, right, bottom, top, near, far);
            return true;
        }

        public bool EnableTexturing(bool bEnable)
        {
            /// TODO: Implement textureing enable function for Direct3D
            return true;
        }

        public bool EnableShading(SHADE_MODE sm)
        {
            if (sm == SHADE_MODE.Flat)
                device.RenderState.ShadeMode = ShadeMode.Flat;
            else
                device.RenderState.ShadeMode = ShadeMode.Gouraud;

            return true;
        }

        public bool EnableDepthTest(bool bEnable)
        {
            device.RenderState.ZBufferEnable = true;
            device.RenderState.ZBufferWriteEnable = true;
            return true;
        }

        public bool EnableDithering(bool bEnable)
        {
            device.RenderState.DitherEnable = bEnable;
            return true;
        }

        public bool EnableBlending(bool bEnable)
        {
            device.RenderState.AlphaBlendEnable = bEnable;
            return true;
        }

        public bool Clear(bool bClrColor, bool bClrDepth)
        {
            ClearFlags cf = 0;
            if (bClrColor)
                cf |= ClearFlags.Target;
            if (bClrDepth)
                cf |= ClearFlags.ZBuffer;
            device.Clear(cf, Color.Purple, 1.0f, 0);
            return true;
        }

        public bool DrawString(string s, float x, float y, float z)
        {
            /// TODO: Implement 'DrawString'
            throw new NotImplementedException();
        }

        public IVertexBuffer CreateVertexBuffer(uint numVertices, VertexElementType vet)
        {
            Debug.WriteLine("Free System Memory before VertexBuffer is created : " + device.GetAvailablePoolMem(Pool.SystemMemory).ToString() + " Bytes");
            VertexBufferX vb = new VertexBufferX(numVertices, vet, device);

            vertexBuffers.Add(vb);
            Debug.WriteLine("Free System Memory after VertexBuffer is created : " + device.GetAvailablePoolMem(Pool.SystemMemory).ToString() + " Bytes");

            return vb;
        }

        public bool DrawObject(DRAW_MODE dm, IVertexBuffer ivb, IRenderTextureProvider textureProvider)
        {
            VertexBufferX vbx = (VertexBufferX)ivb;
            int numPrimitives = 0;
            PrimitiveType pt = 0;

            switch (dm)
            {
                case DRAW_MODE.TriangleFan:
                    pt = PrimitiveType.TriangleFan;
                    numPrimitives = (int)vbx.NumberOfVertices - 2;
                    break;

                case DRAW_MODE.TriangleStrip:
                    pt = PrimitiveType.TriangleStrip;
                    numPrimitives = (int)vbx.NumberOfVertices - 2;
                    break;

                default:
                    break;
            }

            vbx.RealizeVertexBuffer(this, true, 0);
            if (textureProvider.Count > 1)
            {
                EnableBlending(false);
                device.SetTexture(0, ((RenderTextureX)textureProvider[0]).Texture);
                device.SetTexture(1, ((RenderTextureX)textureProvider[1]).Texture);
                device.TextureState[0].AlphaOperation = TextureOperation.SelectArg1;
                device.TextureState[0].AlphaArgument1 = TextureArgument.TextureColor;
                device.TextureState[0].ColorOperation = TextureOperation.SelectArg1;
                device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;
                device.TextureState[0].TextureCoordinateIndex = 0;

                device.TextureState[1].ColorOperation = TextureOperation.BlendTextureAlpha;
                device.TextureState[1].ColorArgument1 = TextureArgument.TextureColor;
                device.TextureState[1].ColorArgument2 = TextureArgument.Current;
                device.TextureState[1].AlphaOperation = TextureOperation.SelectArg1;
                device.TextureState[1].AlphaArgument1 = TextureArgument.TextureColor;
                device.TextureState[1].TextureCoordinateIndex = 1;

            }
            else
            {
                EnableBlending(textureProvider[0, RenderTextureProviderState.BlendingEnabled]);
                device.SetTexture(0, ((RenderTextureX)textureProvider[0]).Texture);
                device.SetTexture(1, null);
            }
            device.DrawPrimitives(pt, 0, numPrimitives);

            return true;
        }

        public bool BeginScene()
        {
            device.BeginScene();
            return true;
        }

        public bool EndScene()
        {
            device.EndScene();
            return true;
        }

        public bool SwapBuffers()
        {
            try
            {
                device.Present();
            }
            catch (Exception)
            {
                Debug.WriteLine("Device.Present() Failed. Exception occured.");
            }
            return true;
        }

        public bool ResetObjectMatrix()
        {
            device.Transform.World = Matrix.Identity;
            return true;
        }

        public bool TranslateObject(float x, float y, float z)
        {
            device.Transform.World = Matrix.Multiply(Matrix.Translation(x, y, z), device.Transform.World);
            return true;
        }

        public bool RotateObject(float angle, float x, float y, float z)
        {
            angle = angle * (float)Math.PI / 180.0f;
            device.Transform.World = Matrix.Multiply(Matrix.RotationAxis(new Vector3(x, y, z), angle), device.Transform.World);
            return true;
        }

        public bool ScaleObject(float x, float y, float z)
        {
            device.Transform.World = Matrix.Multiply(Matrix.Scaling(new Vector3(x, y, z)), device.Transform.World);
            return true;
        }

        public IRenderTexture CreateTexture(string path, bool bIsTransparent)
        {
            Debug.WriteLine("Free System Memory before Texture is created : " + device.GetAvailablePoolMem(Pool.SystemMemory).ToString() + " Bytes");
            RenderTextureX r = new RenderTextureX(path, device);

            textures.Add(r);
            Debug.WriteLine("Free System Memory after Texture is created : " + device.GetAvailablePoolMem(Pool.SystemMemory).ToString() + " Bytes");

            return r;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (!Disposed)
            {
                foreach (VertexBufferX vb in vertexBuffers)
                    vb.Dispose();
                foreach (RenderTextureX t in textures)
                    t.Dispose();
                Disposed = true;
            }
        }

        #endregion
    }
}
